// <copyright file="Point2d.cs" company="WorldWind, NASA">
// Copyright (c) 2009-2010 All Right Reserved
// </copyright>
// <author>nasa</author>
// <email>dentoby@gmail.com</email>
// <date>2010-4</date>
// <summary>This is part of Image.NET project.</summary>

namespace SPLWHU.Core.Vectors
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    ///  The summary of Point.
    /// </summary>
    public class Point2d : Geometry
    {
        /// <summary>
        /// The x and y position.
        /// </summary>
        private double x, y;

        /// <summary>
        /// The invalid point.
        /// </summary>
        private static readonly Point2d invalidPoint = new Point2d();

        /// <summary>
        /// Initializes a new instance of the <see cref="Point2d"/> class, a invalid point.
        /// </summary>
        public Point2d()   // empty point
        {
            base.isValid = false;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Point2d"/> class by specifying the x and y coordinates.
        /// </summary>
        /// <param name="xi">The x coordinate.</param>
        /// <param name="yi">The y coordinate.</param>
        public Point2d(double x, double y) // x,y constructor
        {
            this.x = x;
            this.y = y;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Point2d"/> class by copying another point.
        /// </summary>
        /// <param name="p">The point to copy.</param>
        public Point2d(Point2d p) // copy constructor
        {
            this.x = p.x;
            this.y = p.y;
        }

        /// <summary>
        /// Gets or sets the X coordinate.
        /// </summary>
        /// <value>The X coordinate.</value>
        public double X
        {
            get { return this.x; }
            set { this.x = value; }
        }

        /// <summary>
        /// Gets or sets the Y coordinate.
        /// </summary>
        /// <value>The Y coordinate.</value>
        public double Y
        {
            get { return this.y; }
            set { this.y = value; }
        }

        /// <summary>
        /// Gets the distance from this point to the Point(0,0).
        /// </summary>
        /// <value>The distance to the reference point.</value>
        public double Length
        {
            get
            {
                return Math.Sqrt((this.x * this.x) + (this.y * this.y));
            }
        }

        /// <summary>
        /// Gets the invalid point.
        /// </summary>
        /// <value>The invalid point.</value>
        public static Point2d InvalidPoint
        {
            get { return Point2d.invalidPoint; }
        }

        /// <summary>
        /// Gets the centroid of this instance. 
        /// </summary>
        /// <value>The centroid of this instance.</value>
        public override Point2d Centroid
        {
            get { return new Point2d(this); }
        }

        /// <summary>
        /// Gets the corresponding wkb geometry type (<see cref="GeometryType"/>).
        /// </summary>
        /// <value>An int represents the wkb geometry type.</value>
        public override int TypeFlag
        {
            get { return (int)GeometryType.Point; }
        }

        /// <summary>
        /// Gets the point count in this geometry.
        /// </summary>
        /// <value>The point count.</value>
        /// <remarks>
        /// A geometry instance except <see cref="Point2d"/> or <see cref="LineString"/> returns zero.
        /// </remarks>
        public override int PointCount
        {
            get { return 1; }
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="p1">The first point.</param>
        /// <param name="p2">The second point.</param>
        /// <returns>The result of the operator.</returns>
        public static Point2d operator +(Point2d p1, Point2d p2) // addition 2
        {
            return new Point2d(p1.x + p2.x, p1.y + p2.y);
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="p1">The first point.</param>
        /// <param name="p2">The second point.</param>
        /// <returns>The result of the operator.</returns>
        public static Point2d operator -(Point2d p1, Point2d p2) // subtraction 2
        {
            return new Point2d(p1.x - p2.x, p1.y - p2.y);
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="p">The point P.</param>
        /// <returns>The result of the operator.</returns>
        public static Point2d operator -(Point2d p) // negation
        {
            return new Point2d(-p.x, -p.y);
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="p">The point P.</param>
        /// <param name="k">The factor k.</param>
        /// <returns>The result of the operator.</returns>
        public static Point2d operator *(Point2d p, double k) // multiply by real 2
        {
            return new Point2d(p.x * k, p.y * k);
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="k">The factor k.</param>
        /// <param name="p">The point P.</param>
        /// <returns>The result of the operator.</returns>
        public static Point2d operator *(double k, Point2d p) // and its reverse order!
        {
            return new Point2d(p.x * k, p.y * k);
        }

        /// <summary>
        /// Implements the operator /.
        /// </summary>
        /// <param name="p">The point P.</param>
        /// <param name="k">The factor k.</param>
        /// <returns>The result of the operator.</returns>
        public static Point2d operator /(Point2d p, double k) // divide by real 2
        {
            return new Point2d(p.x / k, p.y / k);
        }

        /// <summary>
        /// The inner product of two points.
        /// </summary>
        /// <param name="p1">The point p1.</param>
        /// <param name="p2">The point p2.</param>
        /// <returns>The inner product</returns>
        public static double Dot(Point2d p1, Point2d p2) // inner product 2
        {
            return (p1.x * p2.x) + (p1.y * p2.y);
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="p1">The point p1.</param>
        /// <param name="p2">The point p2.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(Point2d p1, Point2d p2) // equal?
        {
            return Math.Abs(p1.x - p2.x) <= double.Epsilon && Math.Abs(p1.y - p2.y) <= double.Epsilon;
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="p1">The point p1.</param>
        /// <param name="p2">The point p2.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(Point2d p1, Point2d p2) // equal?
        {
            return !(p1 == p2);
        }

        /// <summary>
        /// Appends a point to this geometry.
        /// </summary>
        /// <param name="point">The point to add.</param>
        public override void AddPoint(Point2d point)
        {
            this.x = point.x;
            this.y = point.y;
        }

        /// <summary>
        /// Gets the point in this geometry at the specified index.
        /// </summary>
        /// <param name="index">The point index.</param>
        /// <returns>The point at the specified position.</returns>
        public override Point2d GetPoint(int index)
        {
            if (index != 0)
            {
                throw new VectorException("Can not access the point at {0}", index);
            }

            return this;
        }

        /// <summary>
        /// Gets the bounding box of this geometry.
        /// </summary>
        /// <returns>The bounding box of this geometry.</returns>
        public override BoundingBox GetExtents()
        {
            return new BoundingBox(this, this);
        }

        /// <summary>
        /// Creates a copy of this geometry.
        /// </summary>
        /// <returns>The copy of this geometry.</returns>
        public override Geometry Clone()
        {
            return new Point2d(this.x, this.y);
        }

        /// <summary>
        /// Normalizes this point.
        /// </summary>
        /// <returns>The L2 norm.</returns>
        public double Norm()
        {
            return Math.Sqrt(this.Norm2());
        }

        /// <summary>
        /// Normalizes this point.
        /// </summary>
        /// <returns>squared L2 norm.</returns>
        public double Norm2()
        {
            return (this.x * this.x) + (this.y * this.y);
        }

        /// <summary>
        /// Normalizes this instance.
        /// </summary>
        /// <returns>The normalized point</returns>
        public Point2d Normalize() // normalization
        {
            double n = this.Norm();
            return new Point2d(this.x / n, this.y / n);
        }

        /// <summary>
        /// Computes the distance from this point to another.
        /// </summary>
        /// <param name="p">The other point.</param>
        /// <returns>The distance between two points.</returns>
        public double Distance(Point2d p)
        {
            return Math.Sqrt(((this.x - p.x) * (this.x - p.x)) + ((this.y - p.y) * (this.y - p.y)));
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="o">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        /// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public override bool Equals(object o)
        {
            try
            {
                return (bool)(this == (Point2d)o);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode()
        {
            // not the best algorithm for hashing, but whatever...
            return (int)(this.x * this.y);
        }
    }
}
